// Functions associated with the WorldObject class
//-----------------

//-----------------
// Includes libraries and other files
#include "WorldObject.h"

//-----------------

//-----------------
//// CONSTRUCTOR - WorldObject
WorldObject::WorldObject()
	: mesh_(), file_name_( ""), breakable_( false), destroyed_( false)
{
	position_.x = 0;
	position_.y = 0;
	position_.z = 0;
}

//// CONSTRUCTOR - WorldObject
WorldObject::WorldObject( const float x, const float y, const float z, bool b, bool d)
	: mesh_(), file_name_( ""), breakable_( b), destroyed_( d)
{
	position_.x = x;
	position_.y = y;
	position_.z = z;
}

//// CONSTRUCTOR - WorldObject
WorldObject::WorldObject( const WorldObject& wo)
	: mesh_( wo.get_mesh()), file_name_( wo.get_file_name()), breakable_( wo.breakable()),
	  destroyed_( wo.destroyed()), position_( wo.get_x(), wo.get_y(), wo.get_z())
{}
//-----------------

//-----------------
//// FUNCTION - get_mesh
// return the object's mesh
Mesh* WorldObject::get_mesh() const {
	// Pre-conditions :- none
	assert( true);

	return mesh_;
}

//// FUNCTION - get_file_name
// return the file name  file name (i.e. where the asset is loaded from)
std::string WorldObject::get_file_name() const {
	// Pre-conditions :- none
	assert( true);

	return file_name_;
}

//// FUNCTION - get_x
// retrieve the object's x position
float WorldObject::get_x() const {
	// Pre-conditions :- none
	assert( true);

	if( rotation_ == 360)
		// this option shouldn't be used, but just in case something goes wrong...
		return position_.x;
	else if( rotation_ == 270)
		return( position_.x + get_x_dimension());
	else if( rotation_ == 180)
		return( position_.x + get_x_dimension());
	else if( rotation_ == 90)
		return position_.x;
	else
		return position_.x;
}

//// FUNCTION - get_y
// retrieve the object's y position
float WorldObject::get_y() const {
	// Pre-conditions :- none
	assert( true);

	return position_.y;
}

//// FUNCTION - get_z
// retrieve the object's z position
float WorldObject::get_z() const {
	// Pre-conditions :- none
	assert( true);

	if( rotation_ == 360)
		// this option shouldn't be used, but just in case something goes wrong...
		return position_.z;
	else if( rotation_ == 270)
		return( position_.z );
	else if( rotation_ == 180)
		return( position_.z + get_z_dimension());
	else if( rotation_ == 90)
		return( position_.z + get_z_dimension());
	else
		return position_.z;
}

//// FUNCTION - get_position
// return the object's position as a vector
D3DXVECTOR3 WorldObject::get_position() const {
	// Pre-conditions :- none
	assert( true);

	D3DXVECTOR3 p( get_x(), get_y(), get_z());
	return p;
}

//// FUNCTION - get_true_x
// retrieve the object's x position without taking the object's rotation into account
float WorldObject::get_true_x() const { return position_.x; }

//// FUNCTION - get_true_y
// retrieve the object's y position without taking the object's rotation into account
float WorldObject::get_true_y() const { return position_.y; }

//// FUNCTION - get_true_z
// retrieve the object's z position without taking the object's rotation into account
float WorldObject::get_true_z() const { return position_.z; }

//// FUNCTION - get_true_position
// return the object's position as a vector without taking the object's rotation into account
D3DXVECTOR3 WorldObject::get_true_position() const { return position_; }

//// FUNCTION - get_x_dimension
// retrieve the object's x dimension
float WorldObject::get_x_dimension() const { return dimensions_.x; }

//// FUNCTION - get_y_dimension
// retrieve the object's y dimension
float WorldObject::get_y_dimension() const { return dimensions_.y; }

//// FUNCTION - get_z_dimension
// retrieve the object's z dimension
float WorldObject::get_z_dimension() const { return dimensions_.z; }

//// FUNCTION - get_dimensions
// return the object's dimensions as a vector
D3DXVECTOR3 WorldObject::get_dimensions() const { return dimensions_; }

//// FUNCTION - get_rotation
// return the object's rotation
float WorldObject::get_rotation() const { return rotation_; }

//// FUNCTION - breakable
// return whether or not the object can be destroyed
bool WorldObject::breakable() const { return breakable_; }

//// FUNCTION - get_health_current
// return the object's current health
int WorldObject::get_health_current() const { return health_current_; }

//// FUNCTION - get_health_maximum
// return the object's maximum health
int WorldObject::get_health_maximum() const { return health_maximum_; }

//// FUNCTION - destroyed
// return whether or not the object is destroyed
bool WorldObject::destroyed() const { return destroyed_; }

//// FUNCTION - collision
// check whether or not the position given by the parameter is within the bounds of the object
void WorldObject::collision( D3DXVECTOR3& player_pos) const {
	// Pre-conditions :- none
	assert( true);

	// if the player is within the x and z bounds of the object...
	if( (player_pos.x > get_x() && player_pos.x < get_x() + get_x_dimension()) &&
		(player_pos.z > get_z() && player_pos.z < get_z() + get_z_dimension())) {
			// ... and if the height difference is small...
			float y_diff( get_y() + get_y_dimension() - player_pos.y);
			if( y_diff >= -1.5 && y_diff <= 1.5)
				// ... then move the player up (so they're walking on top of it)
				player_pos.y += y_diff;
			else {
				// ... otherwise move the player back to the object's boundry
				if( player_pos.x > get_x() && player_pos.x < get_x() + get_x_dimension()) {
					// work out which side is closest
				}
			}
	}
}

//// FUNCTION - set_mesh
// set the object's mesh pointer
void WorldObject::set_mesh( Mesh* m) {
	// Pre-conditions :- the pointer must exist
	assert( m != nullptr);

	mesh_ = m;
	file_name_ = m->get_file_name();
}

//// FUNCTION - set_x
// set the object's x position
void WorldObject::set_x( const float& x) {
	// Pre-conditions :- none
	assert( true);

	position_.x = x;
}

//// FUNCTION - set_y
// set the object's y position
void WorldObject::set_y( const float& y) {
	// Pre-conditions :- none
	assert( true);

	position_.y = y;
}

//// FUNCTION - set_z
// set the object's z position
void WorldObject::set_z( const float& z) {
	// Pre-conditions :- none
	assert( true);

	position_.z = z;
}

//// FUNCTION - set_location
// set the object's position
void WorldObject::set_location( const float& x, const float& y, const float& z) {
	// Pre-conditions :- none
	assert( true);

	set_x( x);
	set_y( y);
	set_z( z);
}

//// FUNCTION - set_x_dimension
// set the object's x dimension
void WorldObject::set_x_dimension( const float& x) {
	// Pre-conditions :- none
	assert( true);

	dimensions_.x = x;
}

//// FUNCTION - set_y_dimension
// set the object's y dimension
void WorldObject::set_y_dimension( const float& y) {
	// Pre-conditions :- none
	assert( true);

	dimensions_.y = y;
}

//// FUNCTION - set_z_dimension
// set the object's z dimension
void WorldObject::set_z_dimension( const float& z) {
	// Pre-conditions :- none
	assert( true);

	dimensions_.z = z;
}

//// FUNCTION - set_dimensions
// set the object's dimensions
void WorldObject::set_dimensions( const float& x, const float& y, const float& z) {
	// Pre-conditions :- none
	assert( true);

	set_x_dimension( x);
	set_y_dimension( y);
	set_z_dimension( z);
}

//// FUNCTION - set_rotation
// set the object's rotation
void WorldObject::set_rotation( const int& r) {
	// Pre-conditions :- must be a valid rotation value
	assert( r >= 0 && r <= 360);

	rotation_ = r;
}

//// FUNCTION - set_breakable
// set whether the object can be destroyed or not
void WorldObject::set_breakable( const bool b) {
	// Pre-conditions :- none
	assert( true);

	breakable_ = b;
}

//// FUNCTION - set_health
// set the object's current health
void WorldObject::set_health( const int& h) {
	// Pre-conditions :- none
	assert( true);

	health_current_ = h;

	// if the object's health has completely gone then destroy the object
	if( health_current_ <= 0) {
		health_current_ = 0;
		set_destroyed( true);
	}
}

//// FUNCTION - set_health_maximum
// set the object's maximum health
void WorldObject::set_health_maximum( const int& h) {
	// Pre-conditions :- the value must be valid
	assert( h > 0);

	health_maximum_ = h;
}

//// FUNCTION - set_destroyed
// set whether the object is destroyed or not
void WorldObject::set_destroyed( const bool d) {
	// Pre-conditions :- none
	assert( true);

	destroyed_ = d;
}

//// FUNCTION - render
// output the object's mesh to the device
void WorldObject::render() {
	// Pre-conditions :- none
	assert( true);

	mesh_->render( get_x(), get_y(), get_z(), 1.0f, 1.0f, 1.0f, rotation_);
}

////// FUNCTION - react_to_gravity
//// apply gravity to the object
//void WorldObject::react_to_gravity() {
//	// ############## WARNING! ####################
//	// This function is a 'basis' and is not finished.
//	// Gravity pushes objects down to 0 instead of onto another object.
//	time_ += 0.1;
//
//	set_y( get_y() - (( 0.5 * 9.81) * ( time_ * time_)));
//
//	if( get_y() < 0) {
//		set_y( 0);
//		time_ = 0;
//	}
//}

//// OVERLOADED OPERATOR - ==
bool WorldObject::operator ==( const WorldObject& w) const {
	// Pre-conditions :- none
	assert( true);

	if( get_mesh() == w.get_mesh())
		return true;
	else
		return false;
}